package com.easychat.websocket.netty;

import com.easychat.entity.constants.Constants;
import com.easychat.entity.dto.MessageSendDto;
import com.easychat.entity.dto.WsInitData;
import com.easychat.entity.enums.MessageTypeEnum;
import com.easychat.entity.enums.UserContactApplyStatusEnum;
import com.easychat.entity.enums.UserContactTypeEnum;
import com.easychat.entity.po.ChatMessage;
import com.easychat.entity.po.ChatSessionUser;
import com.easychat.entity.po.UserContactApply;
import com.easychat.entity.po.UserInfo;
import com.easychat.entity.query.*;
import com.easychat.mappers.ChatMessageMapper;
import com.easychat.mappers.ChatSessionUserMapper;
import com.easychat.mappers.UserContactApplyMapper;
import com.easychat.mappers.UserInfoMapper;
import com.easychat.redis.RedisComponent;
import com.easychat.service.ChatSessionUserService;
import com.easychat.utils.JsonUtils;
import com.easychat.utils.StringTools;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.swing.*;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;


/**
 * @Author Dongdong
 * @注释 ws 工具类
 */
@Component
public class ChannelContextUtils {
    private static final Logger logger = LoggerFactory.getLogger(ChannelContextUtils.class);
    // 用户userId 与 channel 绑定，通过hashmap实现
    private static final ConcurrentHashMap<String, Channel> USER_CONTEXT_MAP = new ConcurrentHashMap<>();
    //  groupID 与 channelGroup 绑定
    private static final ConcurrentHashMap<String, ChannelGroup> GROUP_CONTEXT_MAP = new ConcurrentHashMap<>();
    @Resource
    private RedisComponent redisComponent;
    @Resource
    private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;
    @Resource
    private ChatSessionUserMapper<ChatSessionUser, ChatSessionUserQuery> chatSessionUserMapper;
    @Resource
    private ChatMessageMapper<ChatMessage, ChatMessageQuery> chatMessageMapper;

    @Resource
    private UserContactApplyMapper<UserContactApply, UserContactApplyQuery> userContactApplyMapper;


    /**
     * 用户userId 与 channel 绑定
     *
     * @param userId
     * @param channel
     */
    public void addContext(String userId, Channel channel) {
        try {
//            if (channel.localAddress() != null) {
//                // 获取本地地址，并获取端口号
//                SocketAddress localAddr = channel.localAddress();
//                if (localAddr instanceof InetSocketAddress) {
//                    InetSocketAddress inetSocketAddress = (InetSocketAddress) localAddr;
//                    int port = inetSocketAddress.getPort();
//                    System.out.println("Channel port: " + port);
//                }
//            }
            String channelId = channel.id().toString();
            logger.info("channelId:{}", channelId);
            AttributeKey attributeKey = null;
            if (!AttributeKey.exists(channelId)) {
                //如果channelId不存在，则创建
                attributeKey = AttributeKey.newInstance(channelId);
            } else {
                //如果channelId存在，则获取
                attributeKey = AttributeKey.valueOf(channelId);
            }
            //将channelId 和 userId 绑定
            channel.attr(attributeKey).set(userId);
            USER_CONTEXT_MAP.put(userId, channel);
            //连接上之后更新一次心跳
            redisComponent.saveHeartBeat(userId);

            //获取用户的联系人列表
            List<String> userContactList = redisComponent.getUserContactList(userId);
            for (String contactId : userContactList) {
                //如果是群，则加入群组
                if (contactId.startsWith(UserContactTypeEnum.GROUP.getPrefix())) {
                    add2Group(contactId, channel);
                }
            }

            //更新用户最新的连接时间
            UserInfo updateUserInfo = new UserInfo();
            updateUserInfo.setLastLoginTime(new Date());
            userInfoMapper.updateByUserId(updateUserInfo, userId);

            //更新好友发送的消息（ 只查三天内的，根据last off time）
            UserInfo userInfo = userInfoMapper.selectByUserId(userId);
            Long lastOffTime = userInfo.getLastOffTime();

            //如果lastOffTime距 现在 >3 ，只更新3天内的
            if (lastOffTime != null && System.currentTimeMillis() - Constants.MillisSECOUNDS_3DAYS_AGO > lastOffTime) {
                lastOffTime = System.currentTimeMillis() - Constants.MillisSECOUNDS_3DAYS_AGO;
            }
//            logger.info("lastOffTime:{}", lastOffTime);

            /**
             * 1.查询会话信息 所有的会话信息，保证换个设备也能找到会话
             */
            ChatSessionUserQuery chatSessionUserQuery = new ChatSessionUserQuery();
            chatSessionUserQuery.setUserId(userId);
            chatSessionUserQuery.setOrderBy("last_receive_time desc");

            List<ChatSessionUser> chatSessionUsers = chatSessionUserMapper.selectList(chatSessionUserQuery);

            WsInitData wsInitData = new WsInitData();
            wsInitData.setChatSessionList(chatSessionUsers);

            /**
             * 2.查询离线的聊天消息+ 查询三天内的聊天消息
             * 将chat_message 中 contactId 为自己的记录 和 自己加入的群的id的记录收集
             */
            //TODO 缓存离线消息
            //MsgContactIdList 为 chat_message中自己接收到的消息的id集合
            List<String> MsgContactIdList = userContactList.stream().
                    filter(contactId -> contactId.startsWith(UserContactTypeEnum.GROUP.getPrefix()))
                    .collect(Collectors.toList());   //将自己加入的群id 加入到列表中
            MsgContactIdList.add(userId);//将自己的id加入到列表中

            ChatMessageQuery chatMessageQuery = new ChatMessageQuery();
            chatMessageQuery.setContactIdList(MsgContactIdList);
            chatMessageQuery.setLastReceiveTime(lastOffTime);// 实现查询离线后的消息
            List<ChatMessage> chatMessages = chatMessageMapper.selectList(chatMessageQuery);
            wsInitData.setChatMessageList(chatMessages);


            /**
             * 3.查询好友申请
             */
            UserContactApplyQuery userContactApplyQuery = new UserContactApplyQuery();
            userContactApplyQuery.setReceiveUserId(userId);
            userContactApplyQuery.setStatus(UserContactApplyStatusEnum.INIT.getStatus());
            userContactApplyQuery.setLastApplyTimestamp(lastOffTime);//实现过期的申请不查询
            Integer applyCount = userContactApplyMapper.selectCount(userContactApplyQuery);
            wsInitData.setApplyCount(applyCount);

            /**
             * 4.发送消息
             */
            MessageSendDto messageSendDto = new MessageSendDto();
            messageSendDto.setMessageType(MessageTypeEnum.INIT.getType());//初始化状态
            messageSendDto.setContactId(userId);
            messageSendDto.setExtendData(wsInitData);

            sendMsg(messageSendDto, userId);
        } catch (Exception e) {
            logger.error("初始化链接失败", e);
        }


    }

    public void add2Group(String groupId, Channel channel) {
        ChannelGroup group = GROUP_CONTEXT_MAP.get(groupId);
        if (group == null) {
            group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            GROUP_CONTEXT_MAP.put(groupId, group);
        }
        if (channel == null) {
            return;
        }
        group.add(channel);
    }

    //将用户的群 的channel加入到map中
    public void addUser2Group(String userId, String groupId) {
        Channel channel = USER_CONTEXT_MAP.get(userId);
        add2Group(groupId, channel);
    }

    public void removeContext(Channel channel) {
        Attribute<String> attr = channel.attr(AttributeKey.valueOf(channel.id().toString()));
        String userId = attr.get();

        if (!StringTools.isEmpty(userId)) {
            USER_CONTEXT_MAP.remove(userId);
        }
        //删除心跳
        redisComponent.removeHeartBeat(userId);
        //更新用户最新的断开时间
        UserInfo updateUserInfo = new UserInfo();
        updateUserInfo.setLastOffTime(System.currentTimeMillis());
        userInfoMapper.updateByUserId(updateUserInfo, userId);
    }

    public void sendMessage(MessageSendDto messageSendDto) {
        UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(messageSendDto.getContactId());
        switch (contactTypeEnum) {
            case USER:
                send2User(messageSendDto);
                break;
            case GROUP:
                send2Group(messageSendDto);
                break;
        }
    }

    //发送消息给用户
    public void send2User(MessageSendDto messageSendDto) {
        String contactId = messageSendDto.getContactId();
        if (StringTools.isEmpty(contactId)) {
            return;
        }
        //发送消息
        sendMsg(messageSendDto, contactId);
        //强制下线
        if (MessageTypeEnum.FORCE_OFF_LINE.getType().equals(messageSendDto.getMessageType())) {
            closeContact(contactId);
        }
    }

    //强制下线-关闭通道
    public void closeContact(String userId) {
        if (StringTools.isEmpty(userId)) {
            return;
        }
        //清理token
        redisComponent.cleanUserTokenByUserId(userId);
        Channel channel = USER_CONTEXT_MAP.get(userId);
        if (channel == null) {
            return;
        }
        //关闭通道
        channel.close();
    }

    //发送给群组
    public void send2Group(MessageSendDto messageSendDto) {
        String contactId = messageSendDto.getContactId();
        if (StringTools.isEmpty(contactId)) {
            return;
        }
        ChannelGroup channelGroup = GROUP_CONTEXT_MAP.get(contactId);
        if (channelGroup == null) {
            return;
        }
        //发送到群里
        channelGroup.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertObj2Json(messageSendDto)));

        MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByType(messageSendDto.getMessageType());
        //移除群组
        if (MessageTypeEnum.LEAVE_GROUP == messageTypeEnum || MessageTypeEnum.REMOVE_GROUP == messageTypeEnum) {
            String userId = (String) messageSendDto.getExtendData();
            redisComponent.removeUserContact(userId, messageSendDto.getContactId());
            Channel channel = USER_CONTEXT_MAP.get(userId);
            if (channel == null) {
                return;
            }
            channelGroup.remove(channel);
        }
        //解散群聊
        if (MessageTypeEnum.DISSOLUTION_GROUP == messageTypeEnum) {
            GROUP_CONTEXT_MAP.remove(messageSendDto.getContactId());
            channelGroup.close();
        }

    }

    //发送消息
    public static void sendMsg(MessageSendDto messageSendDto, String receiveId) {
        //获取通道
        Channel sendChannel = USER_CONTEXT_MAP.get(receiveId);
        if (sendChannel == null) {
            return;
        }
        //如果是添加好友，则发送给对方默认消息
        if (MessageTypeEnum.ADD_FRIEND_SELF.getType().equals(messageSendDto.getMessageType())) {
            UserInfo userInfo = (UserInfo) messageSendDto.getExtendData();
            messageSendDto.setMessageType(MessageTypeEnum.ADD_FRIEND.getType());
            messageSendDto.setContactId(userInfo.getUserId());
            messageSendDto.setContactName(userInfo.getNickName());
            messageSendDto.setExtendData(null);
        } else {
            messageSendDto.setContactId(messageSendDto.getSendUserId());
            messageSendDto.setContactName(messageSendDto.getSendUserNickName());
        }
        //完成消息的发送！
        sendChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertObj2Json(messageSendDto)));
    }


}
